package com.example.geoaimavlink_android.activity.fragment

import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.graphics.Color
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.geoaimavlink_android.R
import com.example.geoaimavlink_android.utils.DateTimeUtil
import com.geoai.mavlink.geoainet.base.mavlinkcore.engine.GEOAIError
import com.geoai.mavlink.geoainet.base.mavlinkcore.interfaces.CompletionCallback
import com.geoai.mavlink.geoainet.payload.enums.SpeakerRealTimeSenderType
import com.geoai.mavlink.geoainet.payload.info.SS125FileInfo
import com.geoai.mavlink.geoainet.payload.info.SS125RealTimeSenderInfo
import com.geoai.mavlink.geoainet.payload.interfaces.ISS125PlaySpeakerListener
import com.geoai.module.common.thread.ExecutorsManager
import com.geoai.module.common.thread.TaskType
import kotlinx.android.synthetic.main.frag_ss125.btn_delete
import kotlinx.android.synthetic.main.frag_ss125.btn_list_file
import kotlinx.android.synthetic.main.frag_ss125.btn_play
import kotlinx.android.synthetic.main.frag_ss125.btn_rename
import kotlinx.android.synthetic.main.frag_ss125.btn_stop_play
import kotlinx.android.synthetic.main.frag_ss125.btn_upload_tts
import kotlinx.android.synthetic.main.frag_ss125.edt_tts_text
import kotlinx.android.synthetic.main.frag_ss125.img_speaker
import kotlinx.android.synthetic.main.frag_ss125.rcy
import kotlinx.android.synthetic.main.frag_ss125.tv_tts_status
import me.apon.opuscodec.OpusEncoder
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.Future
import kotlin.random.Random


class SS125Fragment: BaseFragment() {

    private val SAMPLE_RATE = 16000//采样率

    private val FRAME_SIZE = 320//包大小

    private val NUM_CHANNELS = 1//声道数

    private val pcmBuf = ShortArray(FRAME_SIZE * NUM_CHANNELS)

    private var mAudioRecordThread: Future<*>? = null

    private var mOpusFileOutputStream: FileOutputStream? = null

    private var mAudioRecorder: AudioRecord? = null

    private val mPlayerFileInfo = mutableListOf<SS125PlayFile>()

    private val mAdapter by lazy { RcyAdapter(mPlayerFileInfo) }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.frag_ss125, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.RECORD_AUDIO), 1)
        }
        initView()
        initRecycleView()
        initSpeaker()
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun initSpeaker() {
        img_speaker.setOnTouchListener { _, event ->
            when(event.action) {
                MotionEvent.ACTION_DOWN -> {
                    requireActivity().runOnUiThread { showToast("START RECORD.....") }
                    startThreadRecord()
                }
                MotionEvent.ACTION_UP -> {
                    requireActivity().runOnUiThread { showToast("STOP") }
                    stopRecord()
                }
            }
            true
        }
    }

    @SuppressLint("MissingPermission")
    private fun startThreadRecord() {

        val opusFilePath = File(requireContext().externalCacheDir!!.path + File.separator + "AA.opus")

        opusFilePath.delete()

        mOpusFileOutputStream = FileOutputStream(opusFilePath)

        val minBufSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT)

        mAudioRecorder = AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufSize).also {
            it.startRecording()
        }

        val encoder = OpusEncoder()

        encoder.init(SAMPLE_RATE, NUM_CHANNELS, FRAME_SIZE, OpusEncoder.OPUS_APPLICATION_VOIP)

        mAudioRecordThread = ExecutorsManager.get().submit(javaClass.name, TaskType.BACKGROUND) {

            while (!Thread.interrupted()) {
                mAudioRecorder?.read(pcmBuf, 0, pcmBuf.size)

                val enBuf = ByteArray(80)

                encoder.encode(pcmBuf, enBuf)

                mOpusFileOutputStream?.write(enBuf)
            }
        }
    }

    private fun stopRecord() {
        mAudioRecordThread?.cancel(true)
        mOpusFileOutputStream?.flush()
        mOpusFileOutputStream?.close()
        mAudioRecorder?.stop()
        mAudioRecorder?.release()

        getProduct()?.payloadManager?.speaker?.startPushingOpusToSpeaker(
            File("/sdcard/Android/data/com.example.geoaimavlink_android/cache/AA.opus"),
            true,
            object : ISS125PlaySpeakerListener<SS125RealTimeSenderInfo> {
                override fun onProcess(type: SS125RealTimeSenderInfo?) {
                    Log.i("Ronny", "tag: ${type?.speakerRealTimeSenderType?.name} ---process: ${type?.transProgress}")
                }

                override fun onResult(
                    isSuccess: Boolean,
                    type: SS125RealTimeSenderInfo?,
                    geoaiError: GEOAIError?
                ) {

                }
            })
    }

    private fun initRecycleView() {
        rcy.layoutManager = LinearLayoutManager(requireContext())
        rcy.adapter = mAdapter
    }

    private fun initView() {
        btn_stop_play.setOnClickListener {
            getProduct()?.payloadManager?.speaker?.stopPlay {
                showToast("stop: ${it == null}")
            }
        }
        btn_play.setOnClickListener {
            var index = -1
            for (sS125PlayFile in mPlayerFileInfo) {
                if (sS125PlayFile.isSelected) index = sS125PlayFile.file.index
            }
            if (index == -1) return@setOnClickListener
            getProduct()?.payloadManager?.speaker?.startPlayByMediaIndex(index, true) {geoaiError ->
                showToast("ret: ${geoaiError?.description}")
            }
        }
        btn_rename.setOnClickListener {
            var index = -1
            for ((index1, sS125PlayFile) in mPlayerFileInfo.withIndex()) {
                if (sS125PlayFile.isSelected) index = index1
            }
            if (index == -1) return@setOnClickListener

            val newFileName = DateTimeUtil.getInstance().formatDateTime(System.currentTimeMillis(), 14) + Random(10).nextInt()

            getProduct()?.payloadManager?.speaker?.renameSpeakerMedia(mPlayerFileInfo[index].file, newFileName) { geoaiError ->
                showToast("ret: ${geoaiError?.description}")
            }
        }
        btn_delete.setOnClickListener {
            var index = -1
            for ((index1, sS125PlayFile) in mPlayerFileInfo.withIndex()) {
                if (sS125PlayFile.isSelected) index = index1
            }
            if (index == -1) return@setOnClickListener
            getProduct()?.payloadManager?.speaker?.removeSpeakerMedia(mPlayerFileInfo[index].file) { geoaiError ->
                showToast("ret: ${geoaiError?.description}")
            }
        }
        btn_list_file.setOnClickListener {
            getProduct()?.payloadManager?.speaker?.getFileListSnapshot(object : CompletionCallback.ICompletionCallbackWith<MutableList<SS125FileInfo>> {
                override fun onFailure(geoaiError: GEOAIError?) {
                    showToast("error: ${geoaiError?.description}")
                }

                override fun onResult(t: MutableList<SS125FileInfo>?) {
                    mPlayerFileInfo.clear()
                    t?.forEach {
                        mPlayerFileInfo.add(SS125PlayFile(false, it))
                    }
                    mAdapter.notifyDataSetChanged()
                }
            })
        }
        btn_upload_tts.setOnClickListener {
            getProduct()?.payloadManager?.speaker?.startPushingTTSToSpeaker(edt_tts_text.text.toString(), true, object :
                ISS125PlaySpeakerListener<SS125RealTimeSenderInfo> {
                override fun onProcess(type: SS125RealTimeSenderInfo?) {
                    requireActivity().runOnUiThread {
                        tv_tts_status?.text = type?.speakerRealTimeSenderType?.name
                        Log.i("Ronny", "tag: ${type?.speakerRealTimeSenderType?.name} ---process: ${type?.transProgress}")
                    }
                }

                override fun onResult(
                    isSuccess: Boolean,
                    type: SS125RealTimeSenderInfo?,
                    geoaiError: GEOAIError?
                ) {
                    showToast("play: $isSuccess")
                    tv_tts_status?.text = "--"
                }
            })
        }
    }

    class RcyAdapter(private val infoList: MutableList<SS125PlayFile>) : RecyclerView.Adapter<RcyAdapter.VH>() {

        class VH(itemView: View) : RecyclerView.ViewHolder(itemView) {
            val llMenu: LinearLayout = itemView.findViewById(R.id.ll_menu)
            val tvIndex: TextView = itemView.findViewById(R.id.tv_index)
            val tvName: TextView = itemView.findViewById(R.id.tv_name)
            val tvSize: TextView = itemView.findViewById(R.id.tv_size)
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
            return VH(LayoutInflater.from(parent.context).inflate(R.layout.item_ss125_recycle, parent, false))
        }

        override fun getItemCount(): Int = infoList.size

        override fun onBindViewHolder(holder: VH, position: Int) {
            holder.llMenu.setBackgroundColor(if (infoList[position].isSelected) Color.GRAY else Color.TRANSPARENT)
            holder.tvIndex.text = infoList[position].file.index.toString()
            holder.tvName.text = infoList[position].file.fileName
            holder.tvSize.text = infoList[position].file.fileSize.toString()

            holder.llMenu.setOnClickListener {
                infoList.forEach {
                    it.isSelected = false
                }
                infoList[position].isSelected = true
                notifyDataSetChanged()
            }
        }
    }

    data class SS125PlayFile(var isSelected: Boolean, val file: SS125FileInfo)
}